/*
 * =====================================================================================
 *
 *       Filename:  rdma_tc.c
 *
 *    Description: RDMA flow control in software in node 
 *
 *        Version:  1.0
 *        Created:  07/22/2024 09:47:16 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Yuan Yulei (yyl), yyl_nudt@qq.com
 *   Organization:  National University Defense of Technology
 *
 * =====================================================================================
 */

#include "rdma_tc.h"

//#ifndef _RDMA_FC

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>


#include "trace.h"

struct flow_control fc_param1[QPN_NUM_MAX];
struct flow_control fc_param2[QPN_NUM_MAX];
struct flow_control fc_now1[QPN_NUM_MAX];
struct flow_control fc_now2[QPN_NUM_MAX];

bool config_read = false;


/**
 * 协议栈加载时, 读取配置文件, 并初始化流控参数
 */
int
rdma_tc_read_config(void)
{
	char rawBuf[1024];
	char buf[1024];
	char skip;
	char* value;
	char* oldBuf = rawBuf;
	char* newBuf = buf;
	char iniFileName[512];	
	sprintf(iniFileName, "/usr/local/include/infiniband/rdma_tc.conf");
	FILE* iniFile = fopen(iniFileName,"r");

	if(iniFile == NULL)
	{
		printf("Can't open `%s\' file.\n", iniFileName);
		perror("fopen failed");
		return;
	}

	while(!feof(iniFile))
	{
		oldBuf = rawBuf;
		
		fread(oldBuf++,sizeof(char),1,iniFile);

		while(*(oldBuf-1) != '\n' && !feof(iniFile))
		{
			if(*(oldBuf-1) == '\\')
			{
				oldBuf--;

				do
				{
					fread(&skip,sizeof(char),1,iniFile);
				} while(skip != '\n' && !feof(iniFile) );
			}

			fread(oldBuf++,sizeof(char),1,iniFile);
		}
		
		if(!feof(iniFile))
		{
			if(*(oldBuf-1) == '\n')
				*(oldBuf-1) = 0;
			else
				*oldBuf =0;
		}
		else
			*(oldBuf-1) = 0;

		oldBuf = rawBuf;
		newBuf = buf;

		while(*oldBuf)
		{
			if((*oldBuf) != ' ' && (*oldBuf) != '\t')
			{
				*newBuf = *oldBuf;
				newBuf ++;
				oldBuf ++;
			}
			else
				oldBuf ++;
		}

		*newBuf = 0;
		//printf("%s\n",buf);

		if(strncmp(buf,"Time1", strlen("Time1"))==0)
		{
			value = buf + strlen("Time1") + 1;
			fc_param1[0].start_time = atoi(value);
		}
		else if(strncmp(buf,"DatLen1", strlen("DatLen1"))==0)
		{
			value = buf + strlen("DatLen1") + 1;
			fc_param1[0].dat_len = atoi(value);
		}
		else if(strncmp(buf,"PacketNum1", strlen("PacketNum1"))==0)
		{
			value = buf + strlen("PacketNum1") + 1;
			fc_param1[0].pkt_num = atoi(value);
		}
		else if(strncmp(buf,"ExpDatLen1", strlen("ExpDatLen1"))==0)
		{
			value = buf + strlen("ExpDatLen1") + 1;
			fc_param1[0].exp_pkt_len = atoi(value);
		}
		else if(strncmp(buf,"ExpPktNum1", strlen("ExpPktNum1"))==0)
		{
			value = buf + strlen("ExpPktNum1") + 1;
			fc_param1[0].exp_pkt_num = atoi(value);
		}
	}

	fclose(iniFile);

	fc_now1[0].start_time = 0;
	fc_now1[0].periad = 100000;
	fc_now1[0].started = false;
	fc_now1[0].goon = true;
	fc_now1[0].dat_len = 0;
	fc_now1[0].pkt_num = 0;
	fc_now1[0].exp_pkt_len = 0;
	fc_now1[0].exp_pkt_num = 0;
	config_read = true;

	YYL_ROCE_TRACE3(TRACE_ERROR, " %s:%s:%d Config File Read \n", __FILE__, __func__, __LINE__);	
	return 0;
}



/**
 * ibv_post_send操作之前
 * 根据当前控制策略，判断是否可以发送
 */
int
rdma_tc_ctl(struct ibv_qp *qp, struct ibv_send_wr *wr, struct ibv_send_wr **bad_wr)
{
	struct timeval tv;
	gettimeofday(&tv,NULL);
	// 判断是否过了一个控制周期
	unsigned long time_past = tv.tv_sec*1000000 + tv.tv_usec - fc_now1[0].start_time;
	if (time_past > fc_now1[0].periad)
	{
		fc_now1[0].started = true;
		fc_now1[0].start_time = tv.tv_sec*1000000 + tv.tv_usec;
		fc_now1[0].goon = true;
		return true;
	}
	else 
		return fc_now1[0].goon;
}


/**
 * ibv_post_send操作完成后, 更新统计数据
 */
int
rdma_tc_sta(struct ibv_qp *qp, struct ibv_send_wr *wr, struct ibv_send_wr **bad_wr)
{
	// 统计本次发送的消息数量和数据大小
	int pkt_num, dat_len, nreq;
	// 统计每个WR
	for (nreq = 0; wr&&(wr!=*bad_wr); ++nreq, wr = wr->next)
	{
		// 统计每个SGE
		for (int i=0; i<wr->num_sge; i++)
		{
			fc_now1[0].pkt_num ++;
			fc_now1[0].dat_len += wr->sg_list[i].length;
		}
	}
	
	YYL_ROCE_TRACE5(TRACE_ERROR, " %s:%s:%d now pkt_num=%d, dat_len=%d \n", __FILE__, __func__, __LINE__, fc_now1[0].pkt_num, fc_now1[0].dat_len);	
	YYL_ROCE_TRACE5(TRACE_ERROR, " %s:%s:%d param pkt_num=%d, dat_len=%d \n", __FILE__, __func__, __LINE__, fc_param1[0].pkt_num, fc_param1[0].dat_len);	
	if ( fc_now1[0].pkt_num < fc_param1[0].pkt_num &&
		 fc_now1[0].dat_len < fc_param1[0].dat_len )
		fc_now1[0].goon = true;
	else
		fc_now1[0].goon = false;
	YYL_ROCE_TRACE4(TRACE_ERROR, " %s:%s:%d now goon=%d \n", __FILE__, __func__, __LINE__, fc_now1[0].goon);	

	return 0;
}

	 


//#endif
